package com.wuyangnju.lj.simulation.master.service;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import com.wuyangnju.lj.simulation.common.Plan;
import com.wuyangnju.lj.simulation.common.SlaveTask;
import com.wuyangnju.lj.simulation.master.model.ComputionData;
import com.wuyangnju.lj.simulation.master.model.Slave;
import com.wuyangnju.lj.simulation.master.model.Slaves;

public class ComputionService {
	private static final Log log = LogFactory.getLog(ComputionService.class);
	private static final ObjectMapper mapper = new ObjectMapper();

	private static ComputionService singleton = new ComputionService();

	public static ComputionService get() {
		return singleton;
	}

	private ComputionData computionData;
	private Slaves slaves = new Slaves();
	private final ReentrantLock lock = new ReentrantLock(true);
	private ComputionThread computionThread;

	public void initAndStart(List<Plan> plans, double alpha, double theta,
			double k) {
		lock.lock();
		try {
			if (computionThread != null) {
				if (computionThread.isAlive()) {
					computionThread.discard();
				}
			}
			computionData = new ComputionData(plans);
			computionThread = new ComputionThread(computionData, alpha, theta,
					k);
			computionThread.start();
		} catch (Exception e) {
			log.error("init or start compution service fail", e);
		} finally {
			lock.unlock();
		}
		log.info("compution started");
	}

	public void registerSlave(Slave slave) {
		slaves.registerSlave(slave);
	}

	public String getComputionStatus() throws ComputionException {

		if (computionData == null) {
			throw new ComputionException("not yet initialized");
		}
		try {
			return mapper.writeValueAsString(computionData.snapshot());
		} catch (JsonGenerationException e) {
			log.error("compution status json parse error", e);
		} catch (JsonMappingException e) {
			log.error("compution status json parse error", e);
		} catch (IOException e) {
			log.error("compution status json parse error", e);
		}
		return "compution status json parse error";
	}

	public long survivalCount() {
		return computionData.surviveCount();
	}

	public SlaveTask getSlaveTask() throws ComputionException {
		if (computionData == null) {
			throw new ComputionException("not yet initialized");
		}
		List<Plan> plans = computionData.snapshot().currentPlans();
		return new SlaveTask(plans);
	}

	public SlaveTask getSlaveTask(String ip, int threadId)
			throws ComputionException {
		if (computionData == null) {
			throw new ComputionException("not yet initialized");
		}
		List<Plan> plans = computionData.snapshot().currentPlans();
		int fromIndex = (int) Math.round(plans.size()
				* slaves.startPercent(ip, threadId));
		int toIndex = (int) Math.round(plans.size()
				* slaves.endPercent(ip, threadId));
		log.info("ip: " + ip + " threadId: " + threadId + " from: " + fromIndex
				+ " to: " + toIndex);
		log.info(plans.size());
		try {
			return new SlaveTask(plans.subList(fromIndex, toIndex));
		} catch (Exception e) {
			log.error(
					ip + " " + threadId + " " + plans.size() + " "
							+ slaves.startPercent(ip, threadId) + " "
							+ slaves.endPercent(ip, threadId), e);
			return null;
		}

	}

	public long submitDataItem(Plan plan, double dataItem)
			throws ComputionException {
		if (computionData == null) {
			throw new ComputionException("not yet initialized");
		}
		return computionData.addDataItem(plan, dataItem);
	}

}
